#nullable enable

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using PHMEE.PHM.Algorithms.Diagnosis;
using Volo.Abp.Domain.Services;

namespace PHMEE.Managers.Diagnosis;

/// <summary>
/// 主润滑油泵故障诊断
/// </summary>
public class MainLubricatingOilPumpFaultDiagnosisDomainService : DomainService,
    IMainLubricatingOilPumpFaultDiagnosisDomainService
{
    private readonly MainLubricatingOilPumpAlg _mainLubricatingOilPumpAlg;

    private readonly IBearingParametersDomainService _bearingParametersDomainService;

    public MainLubricatingOilPumpFaultDiagnosisDomainService(
        MainLubricatingOilPumpAlg mainLubricatingOilPumpAlg,
        IBearingParametersDomainService bearingParametersDomainService)
    {
        _mainLubricatingOilPumpAlg = mainLubricatingOilPumpAlg;
        _bearingParametersDomainService = bearingParametersDomainService;
    }

    /// <summary>
    /// 主滑油泵故障诊断
    /// </summary>
    /// <param name="pointPosition"></param>
    /// <param name="sampleRate"></param>
    /// <param name="unitWithEquips"></param>
    /// <param name="data"></param>
    /// <exception cref="ArgumentNullException"></exception>
    /// <returns></returns>
    public async Task<string[]> FaultDiagnosisAsync(PointPosition pointPosition, float sampleRate,
        UnitWithEquipsDto unitWithEquips,
        float[] data)
    {
        // 设备参数和阈值初始化
        var equipmentParameters = new MainLubricatingOilPumpEquipmentParameters();
        var thresholds = new MainLubricatingOilPumpThresholds();

        // 验证并提取设备信息
        var (asynchronousElectricMotor, centrifugalPump) = ValidateAndExtractUnitEquipments(unitWithEquips.UnitEquips);

        // 提取异步电动机参数
        ExtractAsynchronousElectricMotorParameters(asynchronousElectricMotor, equipmentParameters);

        // 提取离心泵参数
        ExtractCentrifugalPumpParameters(centrifugalPump, equipmentParameters);

        // 提取轴承信息
        await ExtractBearingParametersAsync(asynchronousElectricMotor, centrifugalPump, equipmentParameters);

        // 提取阈值
        ExtractThresholds(unitWithEquips.Unit, thresholds);

        // 泵端轴承类型
        var type = GetPumpBearingType(centrifugalPump.BearingType);

        // 调用主润滑油泵算法
        var response = await _mainLubricatingOilPumpAlg.MainLubricatingOilPumpAsync(pointPosition, data, sampleRate,
            equipmentParameters, thresholds, type);

        return response.FaultCodes.ToArray();
    }

    private static (AsynElectricMotor, CentrifugalPump) ValidateAndExtractUnitEquipments(List<Equipment> unitEquips)
    {
        if (unitEquips.Count == 0)
        {
            throw new InvalidOperationException(nameof(Equipment));
        }

        if ((unitEquips.FirstOrDefault(e => e.Type == EquipmentEnum.AsynElectricMotor)
             ?? throw new InvalidOperationException(nameof(EquipmentEnum.AsynElectricMotor)))
            is not AsynElectricMotor asynchronousElectricMotorEquip)
        {
            throw new InvalidOperationException(nameof(AsynElectricMotor));
        }

        asynchronousElectricMotorEquip.DeserializeProperties();

        if ((unitEquips.FirstOrDefault(e => e.Type == EquipmentEnum.CentrifugalPump)
             ?? throw new InvalidOperationException(nameof(EquipmentEnum.CentrifugalPump)))
            is not CentrifugalPump centrifugalPumpEquip)
        {
            throw new InvalidOperationException(nameof(CentrifugalPump));
        }

        centrifugalPumpEquip.DeserializeProperties();

        return (asynchronousElectricMotorEquip, centrifugalPumpEquip);
    }

    private static void ExtractAsynchronousElectricMotorParameters(AsynElectricMotor asynchronousElectricMotor,
        MainLubricatingOilPumpEquipmentParameters equipmentParameters)
    {
        equipmentParameters.Rpm = (float)asynchronousElectricMotor.MotorSpeed;
        equipmentParameters.Lf = (float)asynchronousElectricMotor.Frequency;
        equipmentParameters.Rbpf = (float)asynchronousElectricMotor.RotorBarsNumber;
        equipmentParameters.Power = (float)asynchronousElectricMotor.Power;
    }

    private static void ExtractCentrifugalPumpParameters(CentrifugalPump centrifugalPump,
        MainLubricatingOilPumpEquipmentParameters equipmentParameters)
    {
        equipmentParameters.Vp = (float)centrifugalPump.VaneNumber;
    }

    private async Task ExtractBearingParametersAsync(AsynElectricMotor asynchronousElectricMotor,
        CentrifugalPump centrifugalPump,
        MainLubricatingOilPumpEquipmentParameters equipmentParameters)
    {
        equipmentParameters.BearMotorDrive.AddRange(await _bearingParametersDomainService.GetBearingParametersAsync(asynchronousElectricMotor.DriveEndBearFactoryFirst, asynchronousElectricMotor.DriveEndBearModelFirst));
        equipmentParameters.BearMotorFree.AddRange(await _bearingParametersDomainService.GetBearingParametersAsync(asynchronousElectricMotor.NonDriveEndBearFactory, asynchronousElectricMotor.NonDriveEndBearModel));
        equipmentParameters.BearPump1.AddRange(await _bearingParametersDomainService.GetBearingParametersAsync(centrifugalPump.PumpEndBearFactoryFirst, centrifugalPump.PumpEndBearModelFirst));
        equipmentParameters.BearPump2.AddRange(await _bearingParametersDomainService.GetBearingParametersAsync(centrifugalPump.PumpEndBearFactorySecond, centrifugalPump.PumpEndBearModelSecond));
    }

    /// <summary>
    /// 从 Unit 提取阈值
    /// </summary>
    /// <param name="unit"></param>
    /// <param name="thresholds"></param>
    /// <exception cref="InvalidOperationException"></exception>
    private static void ExtractThresholds(Unit unit, MainLubricatingOilPumpThresholds thresholds)
    {
        if (unit is LubricatingOilPumpUnit lubricatingOilPumpUnit)
        {
            ExtractThresholdsForLubricatingOilPump(lubricatingOilPumpUnit, thresholds);
        }
        else if (unit is BallastPumpUnit ballastPumpUnit)
        {
            ExtractThresholdsForBallastPump(ballastPumpUnit, thresholds);
        }
        else if (unit is SeaWaterPumpUnit seaWaterPumpUnit)
        {
            ExtractThresholdsForSeaWaterPump(seaWaterPumpUnit, thresholds);
        }
        else
        {
            throw new InvalidOperationException(nameof(Unit));
        }
    }

    /// <summary>
    /// 滑油泵阈值提取
    /// </summary>
    /// <param name="unit"></param>
    /// <param name="thresholds"></param>
    private static void ExtractThresholdsForLubricatingOilPump(LubricatingOilPumpUnit unit,
        MainLubricatingOilPumpThresholds thresholds)
    {
        unit.DeserializeProperties();

        thresholds.Acc = (float)unit.Acc;
        thresholds.PRms.AddRange(new[]
        {
            (float)unit.PrmsA,
            (float)unit.PrmsB,
            (float)unit.PrmsC
        });
        thresholds.Value1X.AddRange(new[]
        {
            (float)unit.Value1XA,
            (float)unit.Value1XB
        });
        thresholds.Bear.AddRange(new[]
        {
            (float)unit.BearA,
            (float)unit.BearB,
            (float)unit.BearC,
            (float)unit.BearD,
            (float)unit.BearE1,
            (float)unit.BearE2,
            (float)unit.BearE3,
            (float)unit.BearE4,
            (float)unit.BearE5
        });
        thresholds.ArmatureFault.AddRange(new[]
        {
            (float)unit.ArmatureFaultA,
            (float)unit.ArmatureFaultB
        });
        thresholds.ImbalanceFault =
            (float)unit.ImbalanceFault;
        thresholds.ResonanceFault =
            (float)unit.ResonanceFault;
        thresholds.StructureLoose =
            (float)unit.StructureLoose;
        thresholds.AfRms.AddRange(new[]
        {
            (float)unit.AFRmsA,
            (float)unit.AFRmsB,
            (float)unit.AFRmsC
        });
        thresholds.Misalignment.AddRange(new[]
        {
            (float)unit.MisalignmentA,
            (float)unit.MisalignmentB
        });
        thresholds.ImpellerFault.AddRange(new[]
        {
            (float)unit.ImpellerFaultA,
            (float)unit.ImpellerFaultB,
            (float)unit.ImpellerFaultC
        });
        thresholds.BushBear.AddRange(new[]
        {
            (float)unit.BushBearA,
            (float)unit.BushBearB
        });
    }

    /// <summary>
    /// 压载泵阈值提取
    /// </summary>
    /// <param name="unit"></param>
    /// <param name="thresholds"></param>
    private static void ExtractThresholdsForBallastPump(BallastPumpUnit unit,
        MainLubricatingOilPumpThresholds thresholds)
    {
        unit.DeserializeProperties();

        thresholds.Acc = (float)unit.Acc;
        thresholds.PRms.AddRange(new[]
        {
            (float)unit.PrmsA,
            (float)unit.PrmsB,
            (float)unit.PrmsC
        });
        thresholds.Value1X.AddRange(new[]
        {
            (float)unit.Value1XA,
            (float)unit.Value1XB
        });
        thresholds.Bear.AddRange(new[]
        {
            (float)unit.BearA,
            (float)unit.BearB,
            (float)unit.BearC,
            (float)unit.BearD,
            (float)unit.BearE1,
            (float)unit.BearE2,
            (float)unit.BearE3,
            (float)unit.BearE4,
            (float)unit.BearE5
        });
        thresholds.ArmatureFault.AddRange(new[]
        {
            (float)unit.ArmatureFaultA,
            (float)unit.ArmatureFaultB
        });
        thresholds.ImbalanceFault =
            (float)unit.ImbalanceFault;
        thresholds.ResonanceFault =
            (float)unit.ResonanceFault;
        thresholds.StructureLoose =
            (float)unit.StructureLoose;
        thresholds.AfRms.AddRange(new[]
        {
            (float)unit.AFRmsA,
            (float)unit.AFRmsB,
            (float)unit.AFRmsC
        });
        thresholds.Misalignment.AddRange(new[]
        {
            (float)unit.MisalignmentA,
            (float)unit.MisalignmentB
        });
        thresholds.ImpellerFault.AddRange(new[]
        {
            (float)unit.ImpellerFaultA,
            (float)unit.ImpellerFaultB,
            (float)unit.ImpellerFaultC
        });
        thresholds.BushBear.AddRange(new[]
        {
            (float)unit.BushBearA,
            (float)unit.BushBearB
        });
    }

    /// <summary>
    /// 海水泵阈值提取
    /// </summary>
    /// <param name="unit"></param>
    /// <param name="thresholds"></param>
    private static void ExtractThresholdsForSeaWaterPump(SeaWaterPumpUnit unit,
        MainLubricatingOilPumpThresholds thresholds)
    {
        unit.DeserializeProperties();

        thresholds.Acc = (float)unit.Acc;
        thresholds.PRms.AddRange(new[]
        {
            (float)unit.PrmsA,
            (float)unit.PrmsB,
            (float)unit.PrmsC
        });
        thresholds.Value1X.AddRange(new[]
        {
            (float)unit.Value1XA,
            (float)unit.Value1XB
        });
        thresholds.Bear.AddRange(new[]
        {
            (float)unit.BearA,
            (float)unit.BearB,
            (float)unit.BearC,
            (float)unit.BearD,
            (float)unit.BearE1,
            (float)unit.BearE2,
            (float)unit.BearE3,
            (float)unit.BearE4,
            (float)unit.BearE5
        });
        thresholds.ArmatureFault.AddRange(new[]
        {
            (float)unit.ArmatureFaultA,
            (float)unit.ArmatureFaultB
        });
        thresholds.ImbalanceFault =
            (float)unit.ImbalanceFault;
        thresholds.ResonanceFault =
            (float)unit.ResonanceFault;
        thresholds.StructureLoose =
            (float)unit.StructureLoose;
        thresholds.AfRms.AddRange(new[]
        {
            (float)unit.AFRmsA,
            (float)unit.AFRmsB,
            (float)unit.AFRmsC
        });
        thresholds.Misalignment.AddRange(new[]
        {
            (float)unit.MisalignmentA,
            (float)unit.MisalignmentB
        });
        thresholds.ImpellerFault.AddRange(new[]
        {
            (float)unit.ImpellerFaultA,
            (float)unit.ImpellerFaultB,
            (float)unit.ImpellerFaultC
        });
        thresholds.BushBear.AddRange(new[]
        {
            (float)unit.BushBearA,
            (float)unit.BushBearB
        });
    }

    /// <summary>
    /// 基础数据 BearingType 与算法的 Type 映射
    /// 由于基础数据中的 BearingType 值与算法的 Type 相同的值的含义不同，需要该方法进行转换
    /// </summary>
    /// <param name="bearingType"></param>
    /// <returns></returns>
    /// <exception cref="InvalidOperationException"></exception>
    private static int GetPumpBearingType(BearingType bearingType)
    {
        return bearingType switch
        {
            // 双滚动（默认）
            BearingType.DoubleRolling => 1,
            // 单滚动
            BearingType.Rolling => 2,
            // 合金 or 滑动
            BearingType.Alloy => 3,
            _ => throw new InvalidOperationException(nameof(BearingType))
        };
    }
}